package com.limetalks;

/*
 * RED5 Open Source Flash Server - http://www.osflash.org/red5
 * 
 * Copyright (c) 2006-2008 by respective authors (see below). All rights reserved.
 * 
 * This library is free software; you can redistribute it and/or modify it under the 
 * terms of the GNU Lesser General Public License as published by the Free Software 
 * Foundation; either version 2.1 of the License, or (at your option) any later 
 * version. 
 * 
 * This library is distributed in the hope that it will be useful, but WITHOUT ANY 
 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A 
 * PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public License along 
 * with this library; if not, write to the Free Software Foundation, Inc., 
 * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 
 */

import java.util.HashSet;
import java.util.Set;

import javax.persistence.EntityManagerFactory;
import javax.persistence.Persistence;

import org.red5.server.adapter.ApplicationAdapter;
import org.red5.server.api.IClient;
import org.red5.server.api.IConnection;
import org.red5.server.api.IScope;
import org.red5.server.api.Red5;
import org.red5.server.api.service.ServiceUtils;

import com.limetalks.dao.UserDAO;
import com.limetalks.data.Stream;
import com.limetalks.data.User;

/**
 * Sample application that uses the client manager.
 * 
 * @author The Red5 Project (red5@osflash.org)
 */
public class Application extends ApplicationAdapter {
	private EntityManagerFactory emf;
	private UserDAO userDAO;
	private static int userId = 0;
	private Set<Stream> streams = new HashSet<Stream>();
	private Set<User> users = new HashSet<User>();
//	private final static String SCOPE_HALL = "hall";
	
	public Application() {
		if(emf == null){
			emf = Persistence.createEntityManagerFactory("LimeTalksUnit");
			userDAO = new UserDAO(emf);
		}
	}
	
	@Override
	public boolean appStart(IScope app) {				
		RemoteMethods methods = new RemoteMethods();
		app.registerServiceHandler("RemoteClass", methods);
		System.out.println("appStart contextPath=" + app.getContextPath() + "; name=" + app.getName());
		return true;
	}

	/** {@inheritDoc} */
    @Override
	public boolean connect(IConnection conn, IScope scope, Object[] params) {
    	System.out.println("connect conn.host=" + conn.getHost() + "; conn.path=" + conn.getPath() + "; scope.contextPath=" + scope.getContextPath() + "; params.size=" + params.length + ";");
       	boolean result = super.connect(conn, scope, params);
    	//change to auth process
    	String userId = conn.getClient().getId();
    	User user = getUser(userId);
    	if(result && user == null){
			user = new User();
//			user.setId(userId);
			user.setId(Application.userId++);
			users.add(user);
    	}    	
		return result;
	}

	/** {@inheritDoc} */
    @Override
	public void disconnect(IConnection conn, IScope scope) {
    	System.out.println("disconnect conn.host=" + conn.getHost() + "; conn.path=" + conn.getPath() + "; scope.contextPath=" + scope.getContextPath() + ";");
    	super.disconnect(conn, scope);
    	String userId = conn.getClient().getId();
    	User user = getUser(userId);
    	if(user != null){
    		users.remove(user);
    	}    		
	}	
	
	@Override
	public boolean roomStart(IScope roomScope)
	{
		System.out.println("roomStart contextPath=" + roomScope.getContextPath() + "; name=" + roomScope.getName());
		if (!super.roomStart(roomScope))
		{
			return false;
		}
		
		// create a new stream		
		Stream stream = new Stream();
		stream.setName(roomScope.getName());		
		streamStart(stream);		
		return true;
//		return super.roomStart(room);
	}
	
	private void streamStart(Stream stream){
		streams.add(stream);		
		// client call:a new room is created
		ServiceUtils.invokeOnAllConnections(getScope(), "onStreamStarted_Client", new Object[] { stream });
	}

	@Override
	public void roomStop(IScope roomScope)
	{
		System.out.println("roomStop contextPath=" + roomScope.getContextPath() + "; name=" + roomScope.getName());
		super.roomStop(roomScope);
		Stream stream = new Stream();
		stream.setName(roomScope.getName());
		streamStop(stream);
//		super.roomStop(room);
	}
	
	private void streamStop(Stream stream){
		streams.remove(stream);
		ServiceUtils.invokeOnAllConnections(getScope(), "onStreamStopped_Client", new Object[] { stream });
	}
	
	
	
	/*@Override
	public synchronized boolean join(IClient client, IScope scope) {
		System.out.println("join client.id=" + client.getId()  + " scope.contextPath=" + scope.getContextPath() + "; scope.name=" + scope.getName());
		return super.join(client, scope);
	}*/

	/*@Override
	public synchronized void leave(IClient client, IScope scope) {
		System.out.println("leave client.id=" + client.getId()  + " scope.contextPath=" + scope.getContextPath() + "; scope.name=" + scope.getName());
		super.leave(client, scope);
	}*/

	/*@Override
	public boolean roomConnect(IConnection connection, Object[] params) {
		System.out.println("roomConnect connection.host=" + connection.getHost() + "; connection.path=" + connection.getPath() + "; params.length=" + params.length);
		return super.roomConnect(connection, params);
	}*/

	/*@Override
	public void roomDisconnect(IConnection connection) {
		System.out.println("roomDisconnect connection.host=" + connection.getHost() + "; connection.path=" + connection.getPath());
		super.roomDisconnect(connection);
	}*/

	@Override
	public boolean roomJoin(IClient client, IScope scope) {
		System.out.println("roomJoin client.id=" + client.getId()  + " scope.contextPath=" + scope.getContextPath() + "; scope.name=" + scope.getName());
		
		if(!super.roomJoin(client, scope)){
			return false;
		}
		
		User user = new User();
//		user.setId(client.getId());
		Stream stream = getStream(scope.getName());
		streamJoin(user, stream);
		return true;
		
//		return super.roomJoin(client, scope);
	}
	
	private void streamJoin(User user, Stream stream){
		stream.getUsers().add(user);
		ServiceUtils.invokeOnAllConnections(getScope(), "onStreamUpdated_Client", new Object[] { stream });
		ServiceUtils.invokeOnAllConnections(getScope(), "onUserStreamJoin_Client", new Object[] { user, stream });
	}

	@Override
	public void roomLeave(IClient client, IScope scope) {
		System.out.println("roomLeave client.id=" + client.getId()  + " scope.contextPath=" + scope.getContextPath() + "; scope.name=" + scope.getName());
		super.roomLeave(client, scope);
		User user = new User();
//		user.setId(client.getId());
		Stream stream = getStream(scope.getName());
		streamLeave(user, stream);
	}
	
	private void streamLeave(User user, Stream stream){
		stream.getUsers().remove(user);
		ServiceUtils.invokeOnAllConnections(getScope(), "onStreamUpdated_Client", new Object[] { stream });
		ServiceUtils.invokeOnAllConnections(getScope(), "onUserStreamLeft_Client", new Object[] { user, stream });
	}

	/*public boolean startAndJoinRoom_Server(String roomName)
	{
		IScope scope = getChildScope(roomName);
		if (scope == null)
		{
			if (!S_CreateRoom(roomName))
			{
				return false;
			}
		}
		return S_JoinRoom(roomName);
	}

	public boolean startRoom_Server(String roomName)
	{
		LRoom room = new LRoom();
		room.setName(roomName);
		if (rooms.contains(room) || !createChildScope(roomName))
		{
			return false;
		}
		return true;
		return createChildScope(roomName);
	}
	
	public void stopRoom_Server(String roomName)
	{
		LRoom room = new LRoom();
		room.setName(roomName);
		rooms.remove(room);
		IScope roomScope = getChildScope(roomName);		
		removeChildScope(roomScope);
	}

	public boolean joinRoom_Server(String roomName)
	{		
		IConnection conn = Red5.getConnectionLocal();
		IClient client = conn.getClient();
		IScope roomScope = getChildScope(roomName);				
		for (LRoom room : rooms) {
			if(room.getName().equals(roomName)){
				LUser user = new LUser();
				user.setId(client.getId());
				room.getUsers().add(user);
			}
		}
		//getScope().disconnect(conn); // leave root
		//return conn.connect(roomScope); // connect to the room
		for (IScope currentScope : client.getScopes()) {
			S_LeaveRoom(currentScope.getName());
		}		
		return join(client, roomScope); // join the room
	}

	public void leaveRoom_Server(String roomName)
	{
		IScope roomScope = getChildScope(roomName);
		if(roomScope != null){
			IConnection conn = Red5.getConnectionLocal();
			IClient client = conn.getClient();
			for (LRoom room : rooms) {
				if(room.getName().equals(roomName)){
					for (LUser user : room.getUsers()) {
						if(user.getId().equals(client.getId())){
							room.getUsers().remove(user);
							break;
						}
					}
				}
			}
	//		roomScope.disconnect(conn); // leave the room
	//		conn.connect(getScope()); // connect to root		
			leave(client, roomScope);
		}
	}*/

	public boolean sendPublicMessage_Server(String msg)
	{
		IConnection conn = Red5.getConnectionLocal();		
		/*String roomName = conn.getScope().getName();
		for (MyRoom room : rooms) {
			if(room.getName().equals(roomName)){
			ServiceUtils.invokeOnAllConnections(conn.getScope(),
					"C_Room_ReceivePublicMessage", new Object[] { msg });
			}
		}*/
		ServiceUtils.invokeOnAllConnections(getParent(), "receivePublicMessage_Client", new Object[] { msg });
		return true;
	}
	
	public boolean sendStreamMessage_Server(String msg)
	{
		IConnection conn = Red5.getConnectionLocal();
		ServiceUtils.invokeOnAllConnections(conn.getScope(),
				"receiveStreamMessage_Client", new Object[] { msg });
		return true;
	}

	public Set<Stream> getStreams_Server()
	{
		return streams;
	}
	
	private Set<Stream> getUserStreams_Server(User user){
		Set<Stream> result = new HashSet<Stream>();
		for (Stream r : streams) {
			for (User u : r.getUsers()) {
				if(u.equals(user)){
					r.getUsers().remove(u);
				}
			}
		}
		return result;
	}
	
	private User getUser(String userId){
		User result = null;
		for (User user : users) {
			if(user.getId().equals(userId)){
				result = user;
				break;
			}
		}
		return result;
	}
	
	private Stream getStream(String streamId){
		Stream result = null;
		for (Stream stream : streams) {
			if(stream.getId().equals(streamId)){
				result = stream;
				break;
			}
		}
		return result;
	}
	
	public User getDbUser(Integer id){
		User user = userDAO.findById(id);
		return user;
	}
	
	public User getDbUser(String email, String password){
		User user = userDAO.findByEmailPassword(email, password);
		return user;
	}
}
